/**************************************************************
*
* M3D_RAND.H
*
* Random operations
**************************************************************/

//
// Independent random generator
//
class m3dRND_GEN {
   ULONG seed;
public:
   m3dRND_GEN (ULONG _seed = 0) : seed(_seed) {};
   
   void   Init          (ULONG _seed) { seed = _seed; }
   ULONG  GetSeed       (void)        { return seed;  }

	int    RndInt        (void);
   int    RndIntNew     (int prev);
   int    RndIntMax     (int max);
   int    RndIntRange   (int a, int b);
   float  RndFloat      (void);
   float  RndFloatMax   (float max);
   float _RndFloatMax   (float max);
   float  RndFloatRange (float a, float b);

   BOOL   IsProbability (float threshold);

   void   RndBasicCoord (float *pA, float *pB);
   void   RndVUnit      (m3dV *v);
   void   RndVUnitConus (const m3dV *vBase, float angle, m3dV *vRand);
   void   RndVCylinder  (const m3dV *vBase, float radius, float height, m3dV *vRand);
   void   RndVTang      (m3dV *norm, m3dV *randDir);
   void   RndTriPoint   (m2dTRI *tri, m2dV *vRand);
   void   RndTriPoint   (m3dTRI *tri, m3dV *vRand);

private:
   ULONG  Next    (void);
};
extern m3dRND_GEN m3dRndGen;


//
// Random value distributions
//
// 1D distribution
//class m3dRND_DISTR : public m3dSPL {
//public:
//   m3dRND_DISTR (void) : m3dSPL(M3D_SPL_UNKNOWN, sizeof(m3dSPL_KP), sizeof(m3dSPL_KP)) {}
//   virtual void   GetValue       (float arg, void *pValue, void *pValueDif = NULL)  const   { ASSERT(FALSE); }
//
//   void           SetMaxNWeights (int nWeightsMax)                                     { AllocSpline(nWeightsMax + 1); }
//   void           SetWeigth      (int weigthNmb, float weigth);
//   virtual void   UnifyWeigths   (void);
//   virtual void   GetSample      (void *sample);
//
//protected:
//   m3dRND_DISTR (int _kpSize, int _valueSize) : m3dSPL(M3D_SPL_UNKNOWN, _kpSize, _valueSize) {}
//};

// Surface distribution
//class m3dRND_DIST_SURF_KP : public  m3dSPL_KP {
//public:
//   m3dTRI   value;
//};
//struct m3dRND_DISTR_SURF_SAMPLE {
//   int      ind;
//   m3dV     point;
//};
//class m3dRND_DISTR_SURF : public m3dRND_DISTR {
//public:
//   m3dRND_DISTR_SURF (void) : m3dRND_DISTR(sizeof(m3dRND_DIST_SURF_KP), sizeof(m3dTRI)) {}
//
//   void         SetWeigth     (int weigthNmb, m3dTRI *pTri);
//   void         SetWeigth     (int weigthNmb, m3dV *v1, m3dV *v2, m3dV *v3);
//   virtual void UnifyWeigths  (void);
//   virtual void GetSample     (void *sample);
//
//   virtual void GetValue      (float arg, void *pValue, void *pValueDif = NULL) const {ASSERT(0); }
//   
//   M3D_SPL_DECLARE_PURE_ACCESS(m3dRND_DIST_SURF_KP)
//};

const int M3D_RAND_MAX        = 0x7fff;

// random value in [0..1] interval
#define m3dRandSeed(seed)	m3dRndGen.Init(seed)
#define  m3dRandom()       m3dRndGen.RndFloat()
#define  m3dRandMax(m)     m3dRndGen.RndFloatMax(m)
#define _m3dRandMax(m)     m3dRndGen._RndFloatMax(m)
#define  m3dRandRange(a,b) m3dRndGen.RndFloatRange(a, b)

#define  m3dRandInt()      m3dRndGen.RndInt()
#define  m3dRandIntMax(m)     m3dRndGen.RndIntMax(m)
#define  m3dRandIntRange(a,b) m3dRndGen.RndIntRange(a,b)
#define  m3dRandIntNew(prev)      m3dRndGen.RndIntNew(prev)

// random selection of several
template <class T>
const T& m3dRandSelect (const T& a, const T& b)
{
   float rnd = m3dRandom();
   if (rnd < 0.5f) {
      return a;
   }
   return b;
}

template <class T>
const T& m3dRandSelect (const T& a, const T& b, const T& c)
{
   float rnd = m3dRandom();
   if (rnd < 1.f/3.f) {
      return a;
   }
   if (rnd < 2.f/3.f) {
      return b;
   }
   return c;
}

template <class T>
const T& m3dRandSelect (const T& a, const T& b, const T& c, const T& d)
{
   float rnd = m3dRandom();
   if (rnd < 0.25f) {
      return a;
   }
   if (rnd < 0.5f) {
      return b;
   }
   if (rnd < 0.75f) {
      return c;
   }
   return d;
}

DWORD m3dVRand(const m3dV &v, DWORD stride = 1); 
int   m3dVRandIntRange(const m3dV &v, int a, int b, DWORD stride = 1); 
float m3dVRandRange(const m3dV &v, float a, float b, DWORD stride = 1);
void  m3dMakeVUnitConusVRandom(const m3dV &v, m3dV *vBase, float angle, m3dV *vRand, DWORD stride = 1);
void  m3dMakeVUnitVRandom(const m3dV &cc, m3dV *v, DWORD stride = 1);  

// m3dRND_EXCLUSIVE class is for selection within [0, size-1]
// it's useful when you select repeatedly from some array
// and you want the same element never go twice in a row
class m3dRND_EXCLUSIVE {
   int topIdx;
   int lastIdx;
public:
   void Init (int size);
   int  Get  (void);
   m3dRND_EXCLUSIVE() : topIdx(0), lastIdx(0) {}
   m3dRND_EXCLUSIVE(int size) : topIdx(0), lastIdx(0) { Init(size); }
};

//
// Make a random unit vector (on a sphere or within a cone)
//
#define  m3dRandBasicCoord(pA, pB)                            m3dRndGen.RndBasicCoord(pA, pB)
#define  m3dMakeVUnitRandom(v)                                m3dRndGen.RndVUnit(v)
#define  m3dMakeVUnitConusRandom(vBase, angle, vRand)         m3dRndGen.RndVUnitConus(vBase, angle, vRand)
#define  m3dMakeVCylinderRandom(vBase, radius, height, vRand) m3dRndGen.RndVCylinder(vBase, radius, height, vRand)
#define  m3dMakeVTangRandom(norm, randDir)                    m3dRndGen.RndVTang(norm, randDir)
#define  m2dMakeTriPointRandom(tri, vRand)                    m3dRndGen.RndTriPoint(tri, vRand)
#define  m3dMakeTriPointRandom(tri, vRand)                    m3dRndGen.RndTriPoint(tri, vRand)

//
// End of file 'M3D_RAND.H'
//
